class CubeSeaSketch < Sketch
  def initialize
    super
    self[:width] = 800
    self[:height] = 600
    self[:type] = OPENGL
  end
  def container
    super do
      object :class => PHelper::Camera3D do
        block do
          background 0
          # Wireframe toggle would be fun!
          noStroke
          ambientLight(0.0, 0.0, 0.3, -2.0, -20.0, 8.0)
          pointLight(0.0, 0.0, 1.0, 10.0, -4.0, 4.0)
          pointLight(0.0, 0.3, 0.5, 1.0, 8.0, 2.0)
        end
        object :class => PHelper::Sound, :bpm => 130 do

          # Blobular thing: slowly growing tentacles out into the field
          object :class => Particles, :sx => 0.2, :sy => 0.2, :sz => 0.2  do
            particle do #:tx => r_s, :ty => r_s, :tz => r_s do
              object :class => Tentacle,
                :rx => Math::PI / 3 * 2
              object :class => Tentacle, 
                :rx => Math::PI / 3 * 4
              object :class => Tentacle 
            end
            block do
              #@ry = Math::PI / 4
              @ry = pi_pulse(500)
              #@rx = pi_pulse(3000)
              #@rz = pi_pulse(4000)
            end
          end

          # Particle system go! This makes a bunch of blue-ish Drops fly around
          object :class => Particles, :gravity => 0.02, :max_age => 60 do
            block do
              # Spew more particles as time goes on, and also more after a recent beat hits
              num_spew = (Math::log(current_frame)).to_f / 4.0 + (@sketch[:beat] || 1) - 1.5
              i = 0
              while i < num_spew do
                i += 1
                mass = r_u + 0.2
                particle :mass => mass, :tx => r_s * 2, :vy => r_u * -1, :vx => r_s / 2.0, :vz => r_s / 4.0 do
                  # Each particle is composed of a single Drop
                  object :class => Drop, :sx => r_u / 2.0 + 0.4, :sy => r_u / 2.0 + 0.4, :sz => r_u / 2.0 + 0.4 do
                    block do
                      # Move around the x axis a little based on current frame and position
                      @tx = Math::sin(current_frame + particle.position.x) / 10.0
                      # Fade out over time
                      @fill.alpha = (1 - (particle.age / 60))
                      if @sketch[:beat] > 0.9
                        if r_u < 0.2
                          # Blink to fully solid on the beat 20% of the time
                          @fill.alpha = 1
                        end
                        # Add some random velocity on the beat, with negative (up) y-axis vector to make drops "bounce" a bit on the beat
                        particle.addVelocity(r_s / 4.0, -1 * r_u / 6.0, r_s / 4.0)
                      end
                    end
                  end
                end
              end
            end
          end
        end
      end
    end
  end
end

# Tentacle works by adding one of itself after the first beat passes
class Tentacle < Sphere
  def initialize sketch, opts = {}, &block
    super sketch, opts, &block
    @started = frame_count
    @depth = (opts[:depth] || 0)
    @beat_count = 0
    @warts = 0
    @pulse_length = (opts[:pulse_length] || 150)
    @size = 0.98 + r_s / 10.0
    @fill ||= Color.new(Red)
    @fill.value += (r_s / 15.0) - 0.02
    @fill.saturation += (r_s / 18.0) - 0.02
  end

  def draw_before
    # TODO: Make the pulsing more beat-dependent
    @sx = @size * (0.9 + (sin_pulse(200 - @depth * 2.0) / 10.0))
    @sz = @size * (0.9 + (sin_pulse(200 - @depth * 2.1) / 10.0))
    @rx += sin_pulse(105) / 100.0 * sketch[:beat]

    if @sketch[:beat] == 1
      @beat_count += 1 

      # Add a wart on half of the beats on "fresh" tentacles
      if r_u > 0.5 and @warts < 6 and not @extended
        object :class => Wart
      end
    end

    # Clear out warts from old tentacles
    if @beat_count > 12
      self.delete_if { |x| x.is_a? Group }
    end

    @fill.alpha = if @beat_count < 4 then 1 - (@sketch[:beat].to_f + 0.1) else 1 end

    if not @extended and @beat_count == 9 and @depth < 40 then
      @extended = true
      if r_u > 0.8 then
        # Branch!
        add_tentacle r_90, r_90
        add_tentacle r_90, r_90
      else
        add_tentacle
      end
    end
    super
  end

  def add_tentacle ry = 0, rz = r_s / 10.0
    object :class => Tentacle,
      :depth => @depth + 1,
      :pulse_length => @pulse_length + r_s * 2,
      :fill => Color.new(@fill),
      :ty => -0.8 + r_s / 10.0,
      :tx => r_s / 6.0,
      :tz => r_s / 6.0,
      :rx => 0.02 + r_s / 10.0,
      :ry => ry,
      :rz => rz,
      :sy => 0.95
  end
end

class Wart < Group
  def initialize sketch, opts = {}, &block
    super sketch, opts, &block
    # Setup for size pulsing on beats
    size = 0.2 * r_u + 0.1
    start_frame = frame_count
    death = random(60,120).to_i

    beats = random(1,9).to_i
    bounce_length = (beats / sketch[:bpm].to_f * sketch[:frame_rate].to_f * 60).to_i

    @rx = r_PI
    @ry = r_PI
    @rz = r_PI

    sphere :fill => Yellow, :tx => 1 do
      block do
        @fill.alpha = parent.fill.alpha
        @x = size * sin_bounce(bounce_length)

        if frame_count > start_frame + death
          delete
        end
      end
    end
  end
end

# Drop makes a spinny particle fly through the world
#
# Size could decay over time?
class Drop < Box
  def initialize *args, &block
    super *args, &block
    @size = (0.1 + timescale.to_f) * r_u
    @fill = Color.new(Blue)
    @fill.hue -= (r_u / 5.0)
    @fill.value -= (r_u / 5.0)
    @fill.saturation -= (r_u / 2.0)
    @fill.alpha = 0.8 + r_u / 4.0
    random_speed
  end

  def timescale
    if frame_count < 7000
      t = frame_count / 7000.0
      t = 1.8 + (r_s / 8) if t > 1.8
    else
      t = (7000 * 2.0 - frame_count) / 7000.0
    end
    return t
  end

  def random_speed
    @r_y_speed = r_s
    @r_y_accel = r_u / 10
    @r_x_speed = r_s
    @r_x_accel = r_u / 10
    @r_z_speed = r_s
    @r_z_accel = r_u / 10
  end

  def adjust_speed
    random_speed if (@sketch[:beat] >= 0.98)
    @r_x_speed += @r_x_accel
    @r_y_speed += @r_y_accel
    @r_z_speed += @r_z_accel
    @r_x_accel = -1 * @r_x_accel if @r_x_speed.abs > 1
    @r_y_accel = -1 * @r_y_accel if @r_y_speed.abs > 1
    @r_z_accel = -1 * @r_z_accel if @r_z_speed.abs > 1
  end

  def adjust_size
    @size -= 0.001
    @x = @y = @z = ((@sketch[:beat] || 0.8) + 0.2) * @size
  end

  def draw_before
    adjust_speed
    adjust_size
    @rz = @r_x_speed
    @ry = @r_y_speed
    @rz = @r_z_speed
    @fill.alpha -= r_u / 5.0
    super
  end
end
